import {
  songExist,
  downloadSong
} from "../../utils/FileDownloadUtil"
import {
  createScopedThreejs
} from 'threejs-miniprogram'
import {
  registerGLTFLoader
} from '../../utils/gltf-loader'
import {
  GameManager
} from "./GameObjects/GameManager"
import {
  loadGLTFTemplate
} from "./GameObjects/Cube.js"
import {
  GameConfig
} from "./GameObjects/GameConfig"
import {
  formatTimeBySec
} from "../../utils/util"

const MusicSyncMargin = 0.1;

const app = getApp()
Page({
  properties: {
    difficulty: null
  },
  data: {
    item: null,
    loadingText: "加载中...",
    duration: -1,
    currentTime: -1,
    currentTimeStr: "00:00",
    durationStr: "00:00",
    isPlaying: false,
    isLoaded: false,
    currentVolume: 3, // 1 - low, 2 - medium, 3 - full volume.
    isMuted: false,
    volumeString: '🔊',
    debug_log: "",
    show_debug: false
  },
  updateVolumeWithIcon() {
    this.getAudioContext().volume = this.data.currentVolume/3.0;

    let volumeString = '🔈';
    if (this.currentVolume == 1) volumeString = '🔈';
    else if (this.currentVolume == 2) volumeString = '🔉';
    else volumeString = '🔊';
    this.setData({
      volumeString: volumeString
    });
  },
  increaseVolume(){
    if(this.data.currentVolume == 3){
      wx.showToast({
        title: '音量已经最大',
        icon: 'none'
      })
    }else{
      this.setData({
        currentVolume: this.data.currentVolume + 1
      })

      this.updateVolumeWithIcon()
    }
  },
  decreaseVolume(){
    if(this.data.currentVolume == 1){
      wx.showToast({
        title: '音量已经最小',
        icon: 'none'
      })
    }else{
      this.setData({
        currentVolume: this.data.currentVolume - 1
      })

      this.updateVolumeWithIcon()
    }
  },
  muteAudio() {
    if (this.data.isMuted) { // Currently muted, resume volume.      
      this.updateVolumeWithIcon()
    } else {
      this.getAudioContext().volume = 0;
      this.setData({
        volumeString: '🔇'
      });
    }

    this.setData({
      isMuted: !this.data.isMuted
    });
  },
  async loadSongFromNetwork() {
    if (!songExist(this.data.item.id)) {
      await downloadSong(this.data.item, (text) => {
        this.setData({
          loadingText: text
        })
      })
    }
    this.setData({
      loadingText: "Loaded!",
      isLoaded: true
    })
  },
  onUnload() {
    this.setData({
      isPlaying: false
    })

    this.getAudioContext()?.destroy()
  },
  onSliderChange(e) {
    this.startTime = null;
    const slideValue = e.detail.value;
    this.offset = slideValue * 1000.0;

    if (!this.data.isPlaying) { // Render once.
      this.updateAndRender(performance.now())
    }

    this.getAudioContext().seek(e.detail.value)
  },
  onHide() {
    this.setData({
      isPlaying: false
    })
  },
  addLog(str){
    if(this.data.show_debug){
      this.setData({
        debug_log: this.data['debug_log'] + "\n" + str
      })
    }
  },
  /**
   * Lifecycle function--Called when page load
   */
  onLoad(options) {
    this.addLog("Page Loaded")
    const eventChannel = this.getOpenerEventChannel();
    const _this = this

    eventChannel && eventChannel.on("acceptSongItem", async (args) => {
      _this.addLog("Get song item:" + args.item.id);
      _this.setData({
        item: args.item,
        diffObj: args.diffObj,
        durationStr: formatTimeBySec(args.item.metadata.duration),
        duration: args.item.metadata.duration
      })
      _this.addLog("Begin to load song from network");
      await _this.loadSongFromNetwork()
      _this.addLog("Loaded song from network");

      const targetPath = app.globalData.songs[args.item.id].path
      const parser = app.globalData.songs[args.item.id].parser
      const songFilePath = targetPath + encodeURI(parser.getSongFileName())
      _this.gameManager = new GameManager(getApp().globalData.songs[args.item.id].parser, args.diffObj.diff);

      _this.addLog("Loading song to audiocontext");
      _this.getAudioContext().src = songFilePath;
      _this.getAudioContext().autoplay = false;

      _this.getAudioContext().onCanplay(() => {
        _this.addLog("Begin to init threejs");
        _this.initThreeJS();
        _this.addLog("Ended loading threejs");
      })
    })
  },
  innerAudioContext: null,
  getAudioContext() {
    if (!this.innerAudioContext) {
      this.innerAudioContext = wx.createInnerAudioContext();

      this.innerAudioContext.onPlay(() => {
        const musicTime = this.innerAudioContext.currentTime * 1000.0;
        const gameTime = this.gameManager.currentTime;

        if (Math.abs(musicTime - gameTime) > MusicSyncMargin * 1000.0) {
          // Adjust game
          this.offset = musicTime;
          console.log("Synced music")
        }
      })
    }
    return this.innerAudioContext;
  },
  playMusic() {

  },
  startTime: null,
  offset: 0,
  updateAndRender: async function (timeStamp) {
    if (this.startTime == null) {
      this.startTime = timeStamp
    }

    const targetTime = timeStamp - this.startTime + this.offset

    this.setData({
      currentTime: targetTime / 1000.0,
      currentTimeStr: formatTimeBySec(targetTime / 1000.0)
    })

    this.gameManager.updateCurrentTime(targetTime);

    this.renderer.render(this.scene, this.camera);
  },
  // 动画循环
  animate: function (timeStamp) {
    if (!this.data.isPlaying) // Stop the animation
    {
      console.log("Stopped the animation!")
      return;
    }

    this.updateAndRender(timeStamp)
    this.canvas.requestAnimationFrame(this.animate);
  },
  togglePlay() {
    this.setData({
      isPlaying: !this.data.isPlaying
    })

    if (this.data.isPlaying) { // Resume the animation
      this.offset = this.data.currentTime * 1000.0;
      this.startTime = null;
      this.canvas.requestAnimationFrame(this.animate);

      this.getAudioContext().play()
    } else {
      this.getAudioContext().pause()
    }
  },
  initThreeJS() {
    const _this = this
    wx.createSelectorQuery()
      .select('#playNoteCanvas')
      .node()
      .exec(async (res) => {
        try {
          const canvas = res[0].node
          // 创建一个与 canvas 绑定的 three.js
          const THREE = createScopedThreejs(canvas)
          // 传递并使用 THREE 变量
          registerGLTFLoader(THREE);

          _this.addLog("Begin to load gltf template");
          await loadGLTFTemplate(THREE)
          _this.addLog("Loaded gltf template");
          // 创建渲染器，并绑定 canvas
          const renderer = new THREE.WebGLRenderer({
            canvas,
            antialias: true
          });
          renderer.setSize(canvas.width, canvas.height);

          // 创建场景
          const scene = new THREE.Scene();
          scene.background = new THREE.Color(0x87ceeb);

          const camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000);
          camera.position.set(...GameConfig.CameraPosition)

          _this.canvas = canvas
          _this.renderer = renderer
          _this.scene = scene
          _this.camera = camera
          
          // 平行光（模拟太阳）：会有阴影和高光
          const light = new THREE.HemisphereLight(0xffffff, 0x444444);
          light.position.set(0, 2, 20);
          scene.add(light);

          _this.gameManager.init(THREE, scene)

          _this.setData({
            isPlaying: true
          })
          canvas.requestAnimationFrame(_this.animate)

          _this.getAudioContext().play()
        } catch (e) {
          _this.addLog("Error load gltf template:" + e.message || 'unknown!');
          console.error(e)
        }
      })
  }
})